package cn.roadtrip.umysqlmodule.transaction;

import io.vavr.control.Try;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;

import java.util.concurrent.Callable;
import java.util.function.Function;

/**
 * 数据库事务操作
 */
@Component
@Scope("prototype")
public class TransactionRollback implements ApplicationContextAware {

    /**
     * 事务错误代码
     */
    public static final String ErrCode = "transaction-Err";

    @Autowired
    private DataSourceTransactionManager transactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;

    /**
     * 执行事务
     *
     * @param transactional  事务
     * @param rollbackmethod 回滚方法
     * @return
     */
    public <T> T transaction(Callable<T> transactional, Function<Throwable, T> rollbackmethod) {
        TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
        Try<T> t = Try
                .ofCallable(() -> {
                    T r = transactional.call();
                    transactionManager.commit(transactionStatus);
                    return r;
                })
                .recover(ex -> {
                    transactionManager.rollback(transactionStatus);
                    try {
                        return rollbackmethod.apply(ex);
                    } catch (Exception e) {
                        e.printStackTrace();
                        return null;
                    }
                });
        return t.get();
    }

    /**
     * 执行事务
     *
     * @param transactional  事务
     * @param rollbackmethod 回滚方法
     * @return
     */
    public <T> T transaction(
            Callable<T> transactional,
            Function<Throwable, T> rollbackmethod,
            int second) {
        PckValue<T> pv = new PckValue();
        ValidTimeOutThread validT = new ValidTimeOutThread(second);
        TransactionThread transactionThread = new TransactionThread(
                transactional,
                rollbackmethod,
                pv,
                validT,
                transactionManager,
                transactionDefinition
        );
        validT.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler(transactionThread, pv));
        validT.start();
        transactionThread.start();
        try {
            transactionThread.join();
            validT.join();
        } catch (Exception e) {
//            System.out.println("在外面捕获了 validT 的异常");
        }
        return pv.getV();
    }

    static class PckValue<T> {

        public void setV(T v) {
            synchronized (this) {
                if (this.v != null)
                    return;
                this.v = v;
            }
        }

        public T getV() {
            return this.v;
        }

        private T v;
    }


    /**
     * 事务线程
     */
    static class TransactionThread<T> extends Thread {
        private Callable<T> transactional;
        private Function<Throwable, T> rollbackmethod;
        private PckValue<T> pv;
        private Thread validT;

        /**
         * @param transactional         执行事务的方法
         * @param rollbackmethod        回滚方法
         * @param pv                    封装的值对象
         * @param validT                验证线程
         * @param transactionManager
         * @param transactionDefinition
         */
        public TransactionThread(
                Callable<T> transactional,
                Function<Throwable, T> rollbackmethod,
                PckValue pv,
                Thread validT,
                DataSourceTransactionManager transactionManager,
                TransactionDefinition transactionDefinition
        ) {
            super();
            this.transactional = transactional;
            this.rollbackmethod = rollbackmethod;
            this.pv = pv;
            this.validT = validT;
            this.transactionManager = transactionManager;
            this.transactionDefinition = transactionDefinition;
        }

        /**
         * 回滚的值
         */
        private T rollbackValue = null;

        public T rollBack(Throwable e) {
            if (rollbackValue != null) {
                return this.rollbackValue;
            }
            synchronized (this) {
                if (rollbackValue != null) {
                    return this.rollbackValue;
                }
                try {
                    transactionManager.rollback(transactionStatus);
                } catch (Exception e1) {
//                    System.out.println("！！！rollBack()！！！，但是事务失败");
                }
                try {
                    T o = rollbackmethod.apply(e);
                    this.rollbackValue = o;
                    return o;
                } catch (Exception e1) {
                    return null;
                }
            }
        }

        private DataSourceTransactionManager transactionManager;
        private TransactionDefinition transactionDefinition;
        private TransactionStatus transactionStatus;

        public void run() {
            transactionStatus = transactionManager.getTransaction(transactionDefinition);
            Try<T> t = Try
                    .ofCallable(() -> {
                        T r = transactional.call();
                        transactionManager.commit(transactionStatus);
                        return r;
                    })
                    .recover(ex -> {
                        if (this.pv.getV() != null)
                            return this.pv.getV();
                        return rollBack(ex);
                    });
            this.validT.interrupt();
            this.pv.setV(t.get());
        }
    }

    /**
     * 验证事务超时线程
     */
    static class ValidTimeOutThread<T> extends Thread {
        private int timeout = -1;
        private boolean health = true;

        public void setHealth(boolean h) {
            this.health = h;
        }

        public boolean isHealth() {
            return this.health;
        }

        public ValidTimeOutThread(int timeout) {
            super();
            this.timeout = timeout;
        }

        public void run() {
            try {
                Thread.sleep(timeout * 1000, 100);
            } catch (Exception e) {
                return;
            }
            this.setHealth(false);
            throw new RuntimeException("验证事务线程抛出运行时异常");
        }

    }

    class MyUncaughtExceptionHandler<T> implements Thread.UncaughtExceptionHandler {

        private TransactionThread transactionThread;

        private PckValue<T> pv;

        public MyUncaughtExceptionHandler(TransactionThread transactionThread, PckValue<T> pv) {
            this.transactionThread = transactionThread;
            this.pv = pv;
        }

        // 处理从线程里抛出来的异常。
        public void uncaughtException(Thread t, Throwable e) {
            if (((ValidTimeOutThread) t).isHealth()) {
                return;
            }
            if (this.pv.getV() != null)
                return;
            //先存储回调结果，避免线程异常时，又执行了cacth里的方法
            this.pv.setV((T) this.transactionThread.rollBack(e));
            this.transactionThread.interrupt();
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    private ApplicationContext applicationContext;

}